package com.demo.wrapper.utils;

import com.demo.wrapper.function.IGetter;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;

/**
 * lambda工具类
 *
 * @author arthur_zhou
 */
public class LambdaUtil {

    private static final String GET_PREFIX = "get";
    private static final String IS_PREFIX = "is";

    /**
     * 获取属性名
     *
     * @param fn
     * @return
     */
    public static String getClassPropertiesName(IGetter<?> fn) {
        try {
            SerializedLambda lambda = getSerializedLambda(fn);
            String methodName = lambda.getImplMethodName();
            if (!methodName.startsWith(GET_PREFIX) && !methodName.startsWith(IS_PREFIX)) {
                throw new RuntimeException("无效的getter方法");
            }
            return camelToUnderscore(resolveFieldName(methodName));
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException("通过getter的方法引用获取字段名失败");
        }
    }

    /**
     * 处理获取字段名称
     *
     * @param methodName get方法的名字
     * @return 字段名称
     */
    public static String resolveFieldName(String methodName) {
        if (methodName.startsWith(GET_PREFIX)) {
            methodName = methodName.substring(3);
        } else if (methodName.startsWith(IS_PREFIX)) {
            methodName = methodName.substring(2);
        }
        // 驼峰转下划线
        return camelToUnderscore(methodName);
    }

    /**
     * 关键在于这个方法
     *
     * @param fnc
     * @return
     * @throws Exception
     */
    private static SerializedLambda getSerializedLambda(Serializable fnc) throws Exception {
        Method method = fnc.getClass().getDeclaredMethod("writeReplace");
        method.setAccessible(Boolean.TRUE);
        return (SerializedLambda) method.invoke(fnc);
    }

    /**
     * 驼峰转下划线
     *
     * @param field
     * @return
     */
    private static String camelToUnderscore(String field) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < field.length(); i++) {
            char c = field.charAt(i);
            if (i > 0 && Character.isUpperCase(c)) {
                sb.append("_");
            }
            sb.append(Character.toLowerCase(c));
        }
        return sb.toString();
    }
}
